local addon = LibStub("AceAddon-3.0"):NewAddon("Poppins", "AceConsole-3.0", "AceEvent-3.0")

local LibSharedMedia = LibStub("LibSharedMedia-3.0")

local defaultDB = {
	profile = {
		enabled = true,
		lock = false,
		tooltips = 3,
		buttons = {
			count = 1,
			["**"] = {
				keybinding = false,
				state = "1",
				options = {
					-- display
					swap = true,
					direction = "up",
					padding = 5,
					scale = 1,
					scale2 = 1,
					strata = "MEDIUM",
					strata2 = "HIGH",
					alpha = 1,
					alpha2 = 1,
					hover = false,
					alpha2 = false,
					combat = false,
					combat2 = false,
					-- skin
					skin = {
						false, -- skin
						false, -- gloss
						false, -- backdrop
						{}, -- colors
					},
					-- text
					hotkey = {
						show = true,
						font = "Friz Quadrata TT",
						size = 10,
						justify = "RIGHT",
						x = 17,
						y = 11,
					},
					count = {
						show = true,
						font = "Friz Quadrata TT",
						size = 10,
						justify = "RIGHT",
						x = 17,
						y = -10,
					},
					id = {
						show = true,
						font = "Friz Quadrata TT",
						size = 10,
						justify = "LEFT",
						x = -18,
						y = 11,
					},
					-- actions
					clicks = {
						left = 1, -- left
						right = 2, -- right
						middle = 3, -- middle
					},
				},
				position = {
					false, -- x
					false, -- y
					false, -- relId
					false, -- pointA
					false, -- pointB
				},
				popups = {
					count = 4,
					["**"] = { -- popup
						["**"] = { -- modifier
							type = false,
							item = false,
							spell = false,
							macro = false,
						},
					},
				},
			},
		},
	},
}

local function loadOptions()
	local name = format("%sOptions", addon.name)
	local loaded, reason = LoadAddOn(name)
	if not loaded then
		addon:Print(format("%s: %s", name, _G[format("ADDON_%s", reason or "UNKNOWN_ERROR")]))
	end
end

function addon:OnInitialize()
	self.db = LibStub("AceDB-3.0"):New(self.name.."DB", defaultDB, "char")
	self.db.RegisterCallback(self, "OnProfileChanged")
	self.db.RegisterCallback(self, "OnProfileReset", "OnProfileChanged")
	self:SetEnabledState(self.db.profile.enabled)
	self:RegisterChatCommand(addon.name, loadOptions)

	self.buttons = {}
end

function addon:OnEnable()
	self.db.profile.enabled = true
	self:UpdateAll()
end

function addon:OnDisable()
	self.db.profile.enabled = false
	local buttons = self.buttons
	for i=1,#buttons, -1 do
		buttons:Store()
	end
end

function addon:OnProfileChanged()
	local enabled = self.db.profile.enabled
	if enabled ~= self:IsEnabled() then
		if enabled then
			self:Enable()
		else
			self:Disable()
		end
	else
		self:OnDisable()
		if enabled then
			self:OnEnable()
		end
	end
end

function addon:UpdateAll()
	local buttons = self.buttons
	for i=#buttons, 1, -1 do
		buttons[i]:Store()
	end
	local db = self.db.profile
	for i=1, db.buttons.count do
		buttons[i] = self:NewButton(i, db.buttons[i])
	end
end

function addon:NewButton(id, settings)
	local button = self.buttons[id] or addon.Header:Create()
	button:SetID(id)
	button:ClearAllPoints()
	button:SetPoint("CENTER", UIParent, "CENTER")

	if settings.keybinding then
		button:Execute([[ keybinding = "]] .. settings.keybinding .. [["]])
	else
		button:Execute([[ keybinding = false ]])
	end

	local options = settings.options
	button:Execute([[ swap = ]]			.. tostring(options.swap))
	button:Execute([[ alpha = ]]		.. tostring(options.alpha or 1))
	button:Execute([[ alpha2 = ]]		.. tostring(options.alpha2 or 1))
	button:Execute([[ hover = ]]		.. tostring(options.hover))
	button:Execute([[ hover2 = ]]		.. tostring(options.hover2))
	button:Execute([[ scale = ]]		.. tostring(options.scale or 1))
	button:Execute([[ scale2 = ]]		.. tostring(options.scale2 or 1))
	button:Execute([[ strata = "]]		.. tostring(options.strata) .. [["]])
	button:Execute([[ strata2 = "]]		.. tostring(options.strata2) .. [["]])
	button:Execute([[ padding = ]]		.. tostring(options.padding))

	local direction = options.direction
	local x, y = 0, 0
	if direction == "up" then
		y = 1
	elseif direction == "left" then
		x = 1
	elseif direction == "down" then
		y = -1
	elseif direction == "right" then
		x = -1
	end
	button:Execute([[ x = ]] .. x)
	button:Execute([[ y = ]] .. y)

	local popups = button.popups
	for i=#popups, 1, -1 do
		popup:Store()
	end
	for i=1, settings.popups.count do
		local popup = self:NewPopup(button, i, settings.popups[i], options)
		button:SetFrameRef(tostring(i), popup)
		popups[i] = popup
	end
	button:SetAttribute("state-active", "1")
	button:SetAttribute("state-hover", false)

	return button
end

function addon:NewPopup(parent, id, actions, options)
	local popup = parent.popups[id] or addon.Popup:Create()
	popup:SetID(id)
	popup:SetParent(parent)

	for modifer, attributes in pairs(actions) do
		for action, value in pairs(attributes) do
			self:SetAttribute(format("%s%s*", modifier or "*", action), value)
		end
	end

	local hotkey = popup.hotkey
	if options.hotkey.show then
		hotkey:SetJustifyH(options.hotkey.justify)
		hotkey:ClearAllPoints()
		hotkey:SetPoint(options.hotkey.justify, popup, "CENTER", options.hotkey.x, options.hotkey.y)
		hotkey:SetFont(LibSharedMedia:Fetch("font", options.hotkey.font), options.hotkey.size, "OUTLINE")
		hotkey:Show()
	else
		hotkey:Hide()
	end
	local count = popup.count
	if options.count.show then
		count:SetJustifyH(options.count.justify)
		count:ClearAllPoints()
		count:SetPoint(options.count.justify, popup, "CENTER", options.count.x, options.count.y)
		count:SetFont(LibSharedMedia:Fetch("font", options.count.font), options.count.size, "OUTLINE")
		count:Show()
	else
		count:Hide()
	end
	local id = popup.id
	if options.id.show then
		id:SetJustifyH(options.id.justify)
		id:ClearAllPoints()
		id:SetPoint(options.id.justify, popup, "CENTER", options.id.x, options.id.y)
		id:SetFont(LibSharedMedia:Fetch("font", options.id.font), options.id.size, "OUTLINE")
		id:Show()
	else
		id:Hide()
	end

	popup:Execute([[ buttons = table.new() ]])
	popup:Execute([[ buttons["LeftButton"] = ]] .. tostring(options.clicks.left))
	popup:Execute([[ buttons["RightButton"] = ]] .. tostring(options.clicks.right))
	popup:Execute([[ buttons["MiddleButton"] = ]] .. tostring(options.clicks.middle))

	return popup
end

--utility function: create a widget class
function addon:CreateClass(type, parentClass)
	local class = CreateFrame(type)
	class.metatable = {__index = class}

	if parentClass then
		class = setmetatable(class, {__index = parentClass})
		class.super = parentClass
	end

	function class:Bind(o)
		return setmetatable(o, self.metatable)
	end

	return class
end